package edu.cmu.vlis.datamining.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;

import org.jgrapht.alg.NeighborIndex;
import org.jgrapht.graph.Multigraph;

import edu.cmu.vlis.datamining.utils.NodePair;

public class FeatureExtractor implements Serializable{
	Map<Vertex,Set<Integer>> cascadeFList = null;

	FeatureExtractor(Map<Vertex,Set<Integer>> cascadeFList){
		this.cascadeFList = cascadeFList;
	}
    public Multigraph<Vertex,Edge> computeAll(Multigraph<Vertex,Edge> graph,
            NeighborIndex<Vertex, Edge> neighborIndex){
    	
        FeatureList features = new FeatureList(neighborIndex,cascadeFList);        
        for(Vertex v1 : graph.vertexSet()){
            List<Feature> v1list = new ArrayList<Feature>();

            for(Vertex v2 : graph.vertexSet()){

                if(v2.getVertexId() <= v1.getVertexId() || graph.containsEdge(v1, v2))
                    continue;
                for(IFeatureCalculator fc : features.getFeatureList()) 
                    v1list.add(fc.compute(v1, v2));
                v1.setFeatures(v2, v1list);

            }
        }
        return graph;
    }

    public List<PriorityQueue<NodePair>> computeFeatures(final Multigraph<Vertex,Edge> graph,final Set<Vertex> testVertices,
            NeighborIndex<Vertex, Edge> neighborIndex,final int k){

        List<PriorityQueue<NodePair>> pqs = getPQs(new FeatureList(neighborIndex,cascadeFList).getFeatureList().size());
        int counter = 0;
        for(Vertex v1 : testVertices){
            counter ++;
            if(counter % 1000 == 0)
                System.out.println(testVertices.size() - counter + " more vertices to go.");
            for(Vertex v2 : testVertices){
                if(v2.getVertexId() <= v1.getVertexId() || graph.containsEdge(v1, v2))
                    continue;
                
                NodePair np = new NodePair(v1,v2);
                np.computeFeatures(neighborIndex,cascadeFList);
                for( int i =0; i < pqs.size(); i++){
                    PriorityQueue<NodePair> pq = pqs.get(i);
                    if(pq.size() < k)
                        pq.add(np);
                    else{
                        if(np.getFeatureValOf(i) > pq.peek().getFeatureValOf(i) ){
                            pq.poll();
                            pq.offer(np);
                        }
                    }
                }
            } 
        }
        return pqs;
    }

   /* public List<PriorityQueue<NodePair>> computeCascadeFeatures(final Multigraph<Vertex,Edge> graph,
    				final Set<Vertex> testVertices,
    				NeighborIndex<Vertex, Edge> neighborIndex,
    				final int k){

        List<PriorityQueue<NodePair>> pqs = getPQs(new FeatureList(neighborIndex).getFeatureList().size());
        int counter = 0;
        for(Vertex v1 : testVertices){
            counter ++;
            if(counter % 1000 == 0)
                System.out.println(testVertices.size() - counter + " more vertices to go.");
            for(Vertex v2 : testVertices){

                if(v2.getVertexId() <= v1.getVertexId() || graph.containsEdge(v1, v2))
                    continue;
                NodePair np = new NodePair(v1,v2);
                np.computeCascadeFeatures();
                for( int i =0; i < pqs.size(); i++){
                    PriorityQueue<NodePair> pq = pqs.get(i);
                    if(pq.size() < k)
                        pq.add(np);
                    else{
                        if(np.getFeatureValOf(i) > pq.peek().getFeatureValOf(i) ){
                            pq.poll();
                            pq.offer(np);
                        }
                    }
                }
            } 
        }
        return pqs;
    }*/
    private  List<PriorityQueue<NodePair>> getPQs(int featureCnt){

        List<PriorityQueue<NodePair>> pqs = new ArrayList<PriorityQueue<NodePair>>(featureCnt);

        class NPComparator implements Comparator<NodePair>, Serializable{

            private static final long serialVersionUID = 1L;
            int featureIdx;
            public NPComparator(int i){
                featureIdx = i;
            }
            public int compare(NodePair o1, NodePair o2) {
                if(o1.getFeatureValOf(featureIdx) == o2.getFeatureValOf(featureIdx))
                    return 0;
                return o1.getFeatureValOf(featureIdx) > o2.getFeatureValOf(featureIdx) ? 1 : -1;
            }

        }
        for(int i=0; i < featureCnt; i++)
            pqs.add(new PriorityQueue<NodePair>(8000,new NPComparator(i)));

        return pqs;
    }

    //    private NodePair[] insertnewNP(NodePair np, NodePair[] edges, int feature){
    //
    //        int minIdx = -1;
    //        for(int i=0; i< edges.length; i++){
    //            if(edges[i] == null){
    //                minIdx = i;
    //                break;
    //            }
    //        }
    //        if(minIdx > -1){
    //            edges[minIdx] = np;
    //            return edges;
    //        }
    //        float minScore = edges[0].getFeatureValOf(feature);
    //        for(int i=0; i < edges.length;i++){
    //            float curScore = edges[i].getFeatureValOf(feature);
    //            if(curScore <= minScore){
    //                minScore = curScore;
    //                minIdx = i;
    //            }
    //        }
    //        if(np.getFeatureValOf(feature) > edges[minIdx].getFeatureValOf(feature))
    //            edges[minIdx] = np;
    //        return edges;
    //    }
}

//    public DefaultDirectedWeightedGraph<Vertex,Edge> compute(DefaultDirectedWeightedGraph<Vertex,Edge> graph, IFeatureCalculator featureToCompute){
//
// This is incorrect ArrayList gets created everytime. fix it before using it.
//        for(Vertex v1 : graph.vertexSet()){
//            List<Feature> v1list = new ArrayList<Feature>();
//            for(Vertex v2 : graph.vertexSet()){
//                if(v2.getVertexId() <= v1.getVertexId())
//                    continue;
//                v1list.add(featureToCompute.compute(v1, v2));
//                v1.setFeatures(v2, v1list);
//            }
//        }
//        return graph;
//    }
